`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;
localparam          CH_NUM                  = 1;        // support multi-ch if CH_NUM>1, msg_dat of multi-ch is byte-interleaved 
localparam          RM                      = 32;
localparam          MODE_NUM                = 4;        // support multi-mode if MODE_NUM>1, current mode is specified by mode_idx
localparam          MODE_BW                 = $clog2(MODE_NUM);
localparam[MODE_NUM*32-1:0] R_LIST          = {32'd12 , 32'd16 , 32'd26 , 32'd32 }; // each R must occupy 32 bits width
localparam[MODE_NUM*32-1:0] N_LIST          = {32'd255, 32'd200, 32'd128, 32'd64 }; // each R must occupy 32 bits width
localparam          TM                      = RM/2;
localparam          TM_BW                   = $clog2(TM+1);     // range is [0:TM]

reg                                         rst_n;
reg                                         clk;

reg                 [MODE_BW-1:0]           rsc_mode;   // stay unchanged during the entire coded-word inclue chk_vld
reg                                         msg_vld;
reg                 [8-1:0]                 msg_dat;    // msg_dat of multi-ch is byte-interleaved 
reg                                         chk_vld;    // msg_vld and chk_vld can not overlap each other
reg                                         chk_end;
reg                 [8-1:0]                 cw_ep;
wire                [8-1:0]                 cw_dat;     // coded-word data, msg_dat + chk_dat
wire                                        cw_vld;
wire                                        cw_end;
wire                                        cw_rdy;

wire                                        sx_vld;
wire                [CH_NUM*RM*8-1:0]       sx_coe;
wire                [MODE_BW-1:0]           sx_mode;
wire                                        sx_rdy;

wire                                        err_vld;
wire                                        err_chk;
wire                                        err_end;
wire                [8-1:0]                 err_ep;
reg                                         err_rdy;
wire                [2-1:0]                 err_status;

mailbox #(bit [7:0]) ep_mb;

integer msg_idx, err_idx;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_rsc", 1);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_MSG
    integer i, j;
    integer gap, s, e;

//    reg [16*8-1:0] t;
//    reg [7:0] ai;
    reg [256*8-1:0] m;

    ep_mb = new(0);

    rsc_mode = 0;
    msg_vld  = 0;
    msg_dat  = 0;
    chk_vld  = 0;
    chk_end  = 0;
    cw_ep    = 0;

    err_rdy  = 1;

//    ai = 8'h01;
//    for (i=0; i<16; i=i+1) begin
//        for (j=0; j<16; j=j+1) begin
//            t[j*8+:8] = ai; 
//            ai = u_enc.gf256_mul_f(ai, 8'h02);
//        end
//        $display("%32h:%2h", t, u_enc.gf256_a_f(i*16));
//    end

//    for (i=0; i<256; i=i+1) begin
//         j = u_enc.gf256_a_f(i);
//         m[j*8+:8] = u_enc.gf256_na_f(i);
//    end
//
//    for (i=0; i<16; i=i+1) begin
//        $display("m[%2d*128+:128] = 128'h%32h;", i, m[i*128+:128]);
//    end

    msg_idx = 0;
    @(posedge rst_n);

    repeat(20000) begin
        gap = $urandom_range(0, 10);
        s   = $urandom_range(0, 3);
        e   = $urandom_range(0, R_LIST[s*32+:32]/2);
//        gap = 500;
//        s = 1;
//        e = 4;
        //        gap; s;  e;
        gen_msg_t(gap, s,  e);
        msg_idx = msg_idx + 1;
    end
    #20_000;
    rgrs.one_chk_done("GEN MSG num is done.");
end

rsc_enc #(
        .CH_NUM                         (CH_NUM                         ),	// support multi-ch if CH_NUM>1, msg_dat of multi-ch is byte-interleaved 
        .MODE_NUM                       (MODE_NUM                       ),	// support multi-mode if MODE_NUM>1, current mode is specified by rsc_mode
        .R_LIST                         (R_LIST                         ) 	// each R must occupy 32 bits width
) u_enc ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .rsc_mode                       (rsc_mode                       ),	// stay unchanged during the entire coded-word inclue chk_vld
        .msg_vld                        (msg_vld&cw_rdy                 ),
        .msg_dat                        (msg_dat                        ),	// msg_dat of multi-ch is byte-interleaved 
        .chk_vld                        (chk_vld&cw_rdy                 ),	// msg_vld and chk_vld can not overlap each other
        .chk_end                        (chk_end                        ),

        .cw_dat                         (cw_dat                         ),	// coded-word data, msg_dat + chk_dat
        .cw_vld                         (cw_vld                         ),
        .cw_end                         (cw_end                         )
);

rsc_si #(
        .CH_NUM                         (CH_NUM                         ),	// support multi-ch if CH_NUM>1, msg_dat of multi-ch is byte-interleaved 
        .RM                             (RM                             ),
        .UTAG_BW                        (1                              ),
        .MODE_NUM                       (MODE_NUM                       ) 	// support multi-mode if MODE_NUM>1, current mode is specified by mode_idx
) u_si ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .cw_vld                         (cw_vld                         ),
        .cw_dat                         (cw_dat^cw_ep                   ),	// coded-word data, msg_dat + chk_dat
        .cw_end                         (cw_end                         ),
        .cw_mode                        (rsc_mode                       ),
        .cw_utag                        (1'b0                           ),
        .cw_rdy                         (cw_rdy                         ),

        .sx_vld                         (sx_vld                         ),
        .sx_coe                         (sx_coe                         ),
        .sx_mode                        (sx_mode                        ),
        .sx_utag                        (                               ),
        .sx_rdy                         (sx_rdy                         )
);

wire                                        kp_vld;
wire                [MODE_BW-1:0]           kp_mode;
wire                [TM_BW-1:0]             ex_dgr;
wire                [(TM+1)*8-1:0]          ex_coe;
wire                [TM*8-1:0]              wx_coe;
wire                                        sx_zero;
wire                                        kp_rdy;

rsc_kp #(
        .MEA_NUM                        (1                              ),
        .MODE_NUM                       (MODE_NUM                       ),	// support multi-mode if MODE_NUM>1, current mode is specified by rsc_mode
        .UTAG_BW                        (1                              ),
        .R_LIST                         (R_LIST                         ),
        .N_LIST                         (N_LIST                         )
) u_rsc_kp ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .sx_vld                         (sx_vld                         ),
        .sx_coe                         (sx_coe                         ),
        .sx_mode                        (sx_mode                        ),
        .sx_utag                        (1'b0                           ),
        .sx_rdy                         (sx_rdy                         ),

        .kp_vld                         (kp_vld                         ),
        .kp_mode                        (kp_mode                        ),
        .kp_utag                        (                               ),
        .ex_dgr                         (ex_dgr                         ),
        .ex_coe                         (ex_coe                         ),
        .wx_coe                         (wx_coe                         ),
        .sx_zero                        (sx_zero                        ),
        .kp_rdy                         (kp_rdy                         )
);

rsc_chen #(
        .CH_NUM                         (CH_NUM                         ),	// support multi-ch if CH_NUM>1, msg_dat of multi-ch is byte-interleaved 
        .MODE_NUM                       (MODE_NUM                       ),	// support multi-mode if MODE_NUM>1, current mode is specified by rsc_mode
        .UTAG_BW                        (1                              ),
        .R_LIST                         (R_LIST                         ),
        .N_LIST                         (N_LIST                         )
) u_rsc_chen ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .kp_vld                         (kp_vld                         ),
        .kp_utag                        (1'b0                           ),
        .kp_mode                        (kp_mode                        ),
        .ex_dgr                         (ex_dgr                         ),	// degree     of error position polynomial
        .ex_coe                         (ex_coe                         ),	// coeficient of error position polynomial
        .wx_coe                         (wx_coe                         ),	// coeficient of w(x) 
        .sx_zero                        (sx_zero                        ),	// coded-word is good, its sx_coe is all-zero
        .kp_rdy                         (kp_rdy                         ),

        .err_vld                        (err_vld                        ),
        .err_mode                       (                               ),
        .err_utag                       (                               ),
        .err_chk                        (err_chk                        ),
        .err_end                        (err_end                        ),
        .err_ep                         (err_ep                         ),
        .err_rdy                        (err_rdy                        ),
        .err_zero                       (                               ),
        .err_uncor                      (                               ),
        .err_enum                       (                               )
);

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        ;
    end else if (cw_vld==1'b1) begin
        ep_mb.put(cw_ep);
    end
end

reg                 [8-1:0]                 dly_ep;

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        err_idx <= 0;
    end else if ((err_vld==1'b1) && (err_rdy==1'b1)) begin
        ep_mb.get(dly_ep);
        if (dly_ep!=err_ep) begin
            $error("%0h is not same to %0h", dly_ep, err_ep);
            $stop;
        end

        if (err_end==1'b1) begin
            err_idx <=`U_DLY err_idx + 1;
        end
    end
end

//initial begin:CHK_
//    integer         i;
//
//    @(posedge rst_n);
//
//    forever begin
//        @(posedge clk);
//        while(sch_en==1'b0) begin
//            @(posedge clk);
//        end
//
//        if ($onehot(gnt)==1'b0) begin
//            $error("[ERROR] gnt %b is not one-hot after sch_en", gnt);
//        end
//    end
//end

task gen_msg_t; 
    input integer gap;
    input integer s;
    input integer e;

    integer r, n, k, i;
    reg [254:0] e_bm;
    begin
        r = R_LIST[s*32+:32];
        n = N_LIST[s*32+:32];
        k = n-r;

        e_bm = 255'b0;
        repeat(e) begin
            i = $urandom_range(0, n-1);
            while(e_bm[i]==1'b1) begin
                i = $urandom_range(0, n-1);
            end
            e_bm[i] = 1'b1;
        end

        repeat(gap) begin
            @(posedge clk);
        end
    
        rsc_mode <=`U_DLY s;
        msg_vld  <=`U_DLY 1'b1;
        for (i=0; i<n; i=i+1) begin
            if (i<k) begin
                msg_dat <=`U_DLY msg_dat + 1'd1;
            end
             
            if (i==k) begin
                msg_vld <=`U_DLY 1'b0;
                chk_vld <=`U_DLY 1'b1;
            end

            if (i==n-1) begin
                chk_end <=`U_DLY 1'b1;
            end

            if (e_bm[i]==1'b1) begin
                //cw_ep <=`U_DLY 8'h1a;
                cw_ep <=`U_DLY $urandom_range(1, 255);
            end else begin
                cw_ep <=`U_DLY 8'h0;
            end

            @(posedge clk);
            while(cw_rdy==1'b0) begin
                @(posedge clk);
            end
        end
        chk_vld <=`U_DLY 1'b0;
        chk_end <=`U_DLY 1'b0;
        cw_ep   <=`U_DLY 8'h0;
    end
endtask

endmodule

